
#include <iostream>
#include "ParseTree.h"
#include "Statistics.h"
#include "Optimizer.h"
#include "Schema.h"
#include <string>
#include <fstream>
#include <vector>
#include "DBFile.h"

using namespace std;

extern "C" {
	int yyparse(void);   // defined in y.tab.c
}
extern struct FuncOperator *finalFunction;
extern struct TableList *tables;
extern struct AndList *boolean;
extern struct NameList *groupingAtts; // no group->null
extern struct NameList *attsToSelect; //atts to select 
extern int distinctAtts; // 1 for distinct with no aggregates
extern int distinctFunc; // 1 for aggr func with distinct

extern struct NameList *sortAtts; // used for sorted file
extern struct NameList *table_name; // table name to create
extern struct NameList *file_name; // input text file
extern struct AttsList *col_atts; // Column name and type in create table
extern int selectFromTable; // 1 for selection predicate from table
extern int createTable; // 1 
extern int sortedTable; // 0 =  heap, 1 = sorted 
extern int insertTable; // 1 for insert
extern int dropTable; // 1 
extern int printPlan; // 1 if true
extern int executePlan; // 1 if true
extern struct NameList *outputFileName; // output file for printing plan

struct Session
{
	int print, execute;
	string fileName;
};

void checkSession(struct Session & s);


int main () {

	yyparse();
	Session s;        
	checkSession(s); 

	cout <<"started main\n";
	cout<<" printing plan "<<s.print;
	if (selectFromTable == 1)
	{
		char *statName = "Statistics.txt";
		cout<<" came for select\n";

		Statistics Stats;
		Stats.createStatisticsFile(statName);

		Stats.Read(statName );

		Optimizer Opt(Stats, finalFunction, tables, boolean, groupingAtts, 
				attsToSelect, distinctAtts, distinctFunc, 
				s.print, s.fileName);

		Opt.MakeQueryPlan();
		// Execute the query according to the plan only if asked
		if (s.execute == 1)
			Opt.ExecuteQuery();

		return 0;
	}

	else if (createTable == 1)
	{


		vector <Attribute> colVec;
		string tableName;


		if (table_name != NULL)
			tableName= table_name->name;
		else
		{
			cerr << "\nERROR! table name error from parser!\n";
			return 0;
		}


		AttsList * tempCol = col_atts;
		while (tempCol != NULL)
		{
			Attribute atts;
			atts.name = tempCol->name;
			if (tempCol ->code == 1)
				atts.myType = Double;
			else if (tempCol->code == 2)     //3 for name
				atts.myType = Int;
			else if (tempCol->code == 4)
				atts.myType = String;

			colVec.push_back(atts);

			tempCol = tempCol->next;
		}
		string binFile = tableName + ".bin";

		Schema *schema= NULL;
		DBFile dbFile;
		//check for existence of table in catalaog
		ifstream input_file;
		string startLine, tableLine;

		input_file.open("catalog");
		if (!input_file)
		{
			cout<<"\n catalog file not present \n";
			exit(1);
		}

		bool tableFound = false;
		while (!input_file.eof())
		{
			getline(input_file, startLine);


			if (startLine.compare("BEGIN") == 0)
			{
				getline(input_file, tableLine);

				if (tableLine.compare(tableName) == 0)
					tableFound = true;
			}
		}
		input_file.close();

		if (tableFound)
		{
			cerr<<"\ntable exists already\n";
			exit(1);

		}



		//else part, write into catalog file 

		FILE * outCatalog = fopen ("catalog", "a");
		fprintf (outCatalog , "\nBEGIN\n%s\n%s.tbl", tableName.c_str(), tableName.c_str());


		for (int i = 0; i < colVec.size(); i++)
		{
			fprintf (outCatalog , "\n%s ", colVec[i].name);
			if (colVec[i].myType == Int)
				fprintf (outCatalog , "Int");
			if (colVec[i].myType == Double)
				fprintf (outCatalog , "Double");
			if (colVec[i].myType == String)
				fprintf (outCatalog , "String");
		}
		fprintf (outCatalog , "\nEND\n");
		fclose(outCatalog );


		schema = new Schema("catalog", (char*)tableName.c_str());



		//check for sorted heap file 
		if (sortedTable == 1)
		{

			if (sortAtts == NULL)
			{
				cerr << "\n sorting atts not present  for  sorted table creation!\n";
				exit(1);
			}
			else
			{
				vector <string> sColsVec;
				NameList * tempCol = sortAtts;
				while (tempCol != NULL)
				{
					sColsVec.push_back(tempCol ->name);
					tempCol = tempCol ->next;
				}


				int sortNumAtts = sColsVec.size();

				OrderMaker * orderMaker = new OrderMaker();
				orderMaker->numAtts = sortNumAtts ;

				//check for atts in table, also done in statistics check
				for (int i = 0; i < sortNumAtts ; i++)
				{
					char * sortAtts = (char*)sColsVec.at(i).c_str();
					int loc = 0;
					loc = schema->Find(sortAtts );
					if (loc == -1)
						return 0 ;
					else
					{
						orderMaker ->whichAtts[i] = loc; // number of this col in the schema
						orderMaker ->whichTypes[i] = schema->FindType(sortAtts );
					}
				}

				SortInfo sortInfo;
				sortInfo.myOrder = orderMaker;
				sortInfo.runLength = 100;

				dbFile.Create((char*)binFile .c_str(), sorted, (void*)&sortInfo); 

				delete orderMaker ; 
				orderMaker = NULL;
			}
		}
		else
		{

			dbFile.Create((char*)binFile .c_str(), heap, NULL);
		}

		// Close the DB file
		dbFile.Close();

		// delete schema object
		delete schema;
		schema = NULL;

		cout << "\nTable " << tableName.c_str() << " has been created successfully!\n";
	}

	else if (insertTable == 1)
	{

		cout << "\n Inserting into table \n";
		if (table_name== NULL || file_name == NULL)
		{
			cerr << "\nERROR in getting table-name or file-name !\n";
			exit(1);
		}
		else
		{
			string inptableName = table_name->name;
			string inpFileName = file_name->name;

			char table_path[256]; 
			getcwd(table_path, 256); //gets current directory
			string txtFile = string(table_path) + "/" + inpFileName ;

			string binFile = inpFileName + ".bin";

			DBFile dbFile;
			int ret = dbFile.Open((char*)binFile .c_str());
			if (ret == 0) //check in dbfile
				cerr << "\nERROR! Could not open bin flie for  " << inptableName.c_str();

			Schema outSchema("catalog", (char*)inptableName.c_str());
			dbFile.Load(outSchema, (char*)txtFile .c_str());
			dbFile.Close();

			cout << "\nTable " << inpFileName.c_str() << " has been loaded and " 
				<< binFile.c_str() << " has been created successfully!\n";
			return 0;

		}
	}
	else if (dropTable == 1)
	{

//		cout << "\n Dropping  table \n";
		if (table_name == NULL)
			cout << "\nTable name not given!\n";
		else
		{	
			cout<<"abc"<<endl;
			string tableName = table_name->name;
			cout<<"dropping "<<" "<<tableName<<endl;
			ifstream input_file;         //copy schema from catalaog,excpet for this table,
			string startLine, tableLine;
			vector <string> schema_vect;

			input_file.open("catalog");
			if (!input_file)
				{
				cout<<"\nCatalog not present in system\n";
			return 0;
			}

			bool tableFound = false;
			bool  currtableLines = false;
			while (!input_file.eof())
			{
				getline(input_file, startLine);


				if (currtableLines == false)
				{
			//		     getline(input_file, startLine);
					if (startLine.compare("BEGIN") == 0)
					{
						getline(input_file, tableLine);

						if (tableLine.compare(tableName) == 0)
						{
							currtableLines = true;
							tableFound = true;
						}
						else
						{
							schema_vect.push_back(startLine );
							schema_vect.push_back(tableLine);
						}
					
				}
					else
						schema_vect.push_back(startLine);
				}
				if (currtableLines == true && startLine .compare("END") == 0)
					currtableLines = false;
			}
			input_file.close();
                            
			if (tableFound == false)
			{
				cout<<"\n table not present in system\n";
			return 0;

			}
		
			remove("catalog");


			ofstream output_file;
			output_file.open("catalog");
			for (int i = 0; i < schema_vect.size(); i++)
			{
				output_file << schema_vect.at(i).c_str() << endl;
			}
			output_file.close();

			string binFile = tableName + ".bin";
			remove(binFile .c_str());
			binFile = binFile + "bin.meta";
			remove(binFile .c_str());

			cout  << tableName.c_str() << " has been dropped !\n";
			return 1;

		}
	}
	else
	{
		if (printPlan == 0 && executePlan == 0 && outputFileName == NULL)
			return 0; 

		ofstream session;
		session.open("Session");
		session << printPlan << endl;
		session << executePlan << endl;
		if (outputFileName != NULL)
			session << outputFileName->name << endl;
		else
			session << "NONE" << endl;
		session.close();
		cout << "\n Set output is saved.\n";
	}

}


void checkSession(struct Session & s)
{
	ifstream session;
	session.open("Session");
	if (!session)
		cerr << "\nERROR! create a new session pls!\n\n";
	else
	{
		int printScreen, executePlan;
		string fileName;
		session >> printScreen;
		session >> executePlan;
		session >> fileName;




		s.print = printScreen;
		s.execute = executePlan;
		s.fileName = fileName;

		session.close();
	}
} 












